package com.xtedu.examproject.util;

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class RedisUtil {
    private static RedisClient redisClient;
    private static StatefulRedisConnection<String, String> connection;
    private static RedisCommands<String, String> commands;
    private static final ObjectMapper objectMapper = createObjectMapper();

    // 连接池缓存
    private static final Map<String, RedisUtil> instanceCache = new ConcurrentHashMap<>();

    static {
        init();
    }

    /**
     * 初始化Redis连接
     */
    private static void init() {
        try {
            // 加载配置文件
            Properties props = new Properties();
            InputStream input = RedisUtil.class.getClassLoader().getResourceAsStream("config.properties");
            if (input == null) {
                throw new RuntimeException("找不到配置文件 config.properties");
            }
            props.load(input);

            String host = props.getProperty("redis.host", "localhost");
            int port = Integer.parseInt(props.getProperty("redis.port", "6379"));
            String password = props.getProperty("redis.password", "");
            int database = Integer.parseInt(props.getProperty("redis.database", "0"));
            int timeout = Integer.parseInt(props.getProperty("redis.timeout", "3000"));

            // 构建RedisURI
            RedisURI redisURI = RedisURI.builder()
                    .withHost(host)
                    .withPort(port)
                    .withPassword(password.toCharArray())
                    .withDatabase(database)
                    .withTimeout(Duration.ofMillis(timeout))
                    .build();

            // 创建RedisClient
            redisClient = RedisClient.create(redisURI);
            connection = redisClient.connect();
            commands = connection.sync();

            System.out.println("Redis连接初始化成功: " + host + ":" + port);

        } catch (Exception e) {
            throw new RuntimeException("Redis连接初始化失败", e);
        }
    }

    /**
     * 创建并配置 ObjectMapper 实例，支持 Java 8 时间类型序列化
     *
     * @return 配置好的 ObjectMapper 实例
     */
    public static ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 注册 JavaTimeModule 以支持 LocalDateTime 等时间类型
        objectMapper.registerModule(new JavaTimeModule());
        // 禁用时间戳格式，使用字符串格式序列化日期时间
        objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return objectMapper;
    }

    // ============================== 基本操作 ==============================

    /**
     * 设置字符串值
     */
    public static void set(String key, String value) {
        commands.set(key, value);
    }

    /**
     * 设置带过期时间的字符串值
     */
    public static void setex(String key, String value, long seconds) {
        commands.setex(key, seconds, value);
    }

    /**
     * 获取字符串值
     */
    public static String get(String key) {
        return commands.get(key);
    }

    /**
     * 删除键
     */
    public static boolean del(String key) {
        return commands.del(key) > 0;
    }

    /**
     * 判断键是否存在
     */
    public static boolean exists(String key) {
        return commands.exists(key) > 0;
    }

    /**
     * 设置过期时间
     */
    public static boolean expire(String key, long seconds) {
        return commands.expire(key, seconds);
    }

    // ============================== Hash操作 ==============================

    /**
     * 设置Hash字段值
     */
    public static void hset(String key, String field, String value) {
        commands.hset(key, field, value);
    }

    /**
     * 获取Hash字段值
     */
    public static String hget(String key, String field) {
        return commands.hget(key, field);
    }

    /**
     * 获取整个Hash
     */
    public static Map<String, String> hgetall(String key) {
        return commands.hgetall(key);
    }

    /**
     * 删除Hash字段
     */
    public static boolean hdel(String key, String field) {
        return commands.hdel(key, field) > 0;
    }

    // ============================== List操作 ==============================

    /**
     * 向左添加列表元素
     */
    public static long lpush(String key, String... values) {
        return commands.lpush(key, values);
    }

    /**
     * 向右添加列表元素
     */
    public static long rpush(String key, String... values) {
        return commands.rpush(key, values);
    }

    /**
     * 从左弹出列表元素
     */
    public static String lpop(String key) {
        return commands.lpop(key);
    }

    /**
     * 从右弹出列表元素
     */
    public static String rpop(String key) {
        return commands.rpop(key);
    }

    /**
     * 获取列表范围
     */
    public static List<String> lrange(String key, long start, long stop) {
        return commands.lrange(key, start, stop);
    }

    // ============================== Set操作 ==============================

    /**
     * 添加集合元素
     */
    public static long sadd(String key, String... members) {
        return commands.sadd(key, members);
    }

    /**
     * 获取集合所有元素
     */
    public static Set<String> smembers(String key) {
        return commands.smembers(key);
    }

    /**
     * 判断是否集合成员
     */
    public static boolean sismember(String key, String member) {
        return commands.sismember(key, member);
    }

    // ============================== 高级功能 ==============================

    /**
     * 对象序列化为JSON并存储
     */
    public static void setObject(String key, Object obj) {
        try {
            String json = objectMapper.writeValueAsString(obj);
            set(key, json);
        } catch (Exception e) {
            throw new RuntimeException("对象序列化失败", e);
        }
    }

    /**
     * 对象序列化为JSON并存储，同时设置过期时间（秒）
     */
    public static void setObject(String key, Object obj, long seconds) {
        try {
            String json = objectMapper.writeValueAsString(obj);
            setex(key, json, seconds);
        } catch (Exception e) {
            throw new RuntimeException("对象序列化失败", e);
        }
    }

    /**
     * 获取并反序列化对象
     */
    public static <T> T getObject(String key, Class<T> clazz) {
        try {
            String json = get(key);
            if (json == null) return null;
            return objectMapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new RuntimeException("对象反序列化失败", e);
        }
    }

    /**
     * 获取并反序列化复杂对象（如List、Map等）
     */
    public static <T> T getObject(String key, TypeReference<T> typeReference) {
        try {
            String json = get(key);
            if (json == null) return null;
            return objectMapper.readValue(json, typeReference);
        } catch (Exception e) {
            throw new RuntimeException("对象反序列化失败", e);
        }
    }

    /**
     * 原子递增
     */
    public static long incr(String key) {
        return commands.incr(key);
    }

    /**
     * 原子递减
     */
    public static long decr(String key) {
        return commands.decr(key);
    }

    /**
     * 发布消息
     */
    public static void publish(String channel, String message) {
        commands.publish(channel, message);
    }

    // ============================== 工具方法 ==============================

    /**
     * 获取所有匹配的键
     */
    public static List<String> keys(String pattern) {
        return commands.keys(pattern);
    }

    /**
     * 获取键的类型
     */
    public static String type(String key) {
        return commands.type(key);
    }

    /**
     * 关闭连接
     */
    public static void close() {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (redisClient != null) {
                redisClient.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试连接
     */
    public static boolean ping() {
        try {
            return "PONG".equals(commands.ping());
        } catch (Exception e) {
            return false;
        }
    }
}